/*
 * flash.c
 *
 *  Created on: 2023年12月25日
 *      Author: Administrator
 */


#include "flash.h"

#if STM32_FLASH_WREN	//如果使能了写
static uint64_t STMFLASH_BUF [ STM_SECTOR_SIZE / 8 ];//最多是2K字节
#endif

/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
  * 函数功能: 读取指定地址的半字(16位数据)
  * 输入参数: faddr:读地址(此地址必须为2的倍数!!)
  * 返 回 值: 返回值:对应数据.
  * 说    明：无
  */
uint16_t STMFLASH_ReadHalfWord ( uint32_t faddr )
{
	return *(__IO uint16_t*)faddr;
}

#if STM32_FLASH_WREN	//如果使能了写
/**
  * 函数功能: 不检查的写入
  * 输入参数: WriteAddr:起始地址
  *           pBuffer:数据指针
  *           NumToWrite:双字(32+32位)数
  * 返 回 值: 无
  * 说    明：无
  */
void STMFLASH_Write_NoCheck ( uint32_t WriteAddr, uint64_t * pBuffer, uint16_t NumToWrite )
{
	uint16_t i;

	for(i=0;i<NumToWrite;i++)
	{

		HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,WriteAddr,pBuffer[i]);
//		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
		WriteAddr+=8;                                    //地址增加8. 8个字节
	}
}

/**
  * 函数功能: 从指定地址开始写入指定长度的数据
  * 输入参数: WriteAddr:起始地址(此地址必须为2的倍数!!)
  *           pBuffer:数据指针
  *           NumToWrite:双字(64位)数(就是要写入的64位数据的个数.)
  * 返 回 值: 无
  * 说    明：无
  */
void STMFLASH_Write ( uint32_t WriteAddr, uint64_t * pBuffer, uint16_t NumToWrite )
{
	uint16_t secoff;	   //扇区内偏移地址(16位字计算)
	uint16_t secremain; //扇区内剩余地址(16位字计算)
 	uint16_t i;
	uint32_t secpos;	   //扇区地址
	uint32_t offaddr;   //去掉0X08000000后的地址
	uint32_t _status = 0;

	if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址

	HAL_FLASH_Unlock();						//解锁

	offaddr=WriteAddr-FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/8;		//在扇区内的偏移(8个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/8-secoff;		//扇区剩余空间大小
	if(NumToWrite<=secremain) {
		secremain=NumToWrite;//不大于该扇区范围
	} else {
		secremain = STM_SECTOR_SIZE/8;
	}

	while(1)
	{
		_status = EraseFlash(secpos,1);//擦除这个扇区
		if(_status != 0xFFFFFFFF){
			printf("EraseFlash %#x is error!\r\n",_status);
		}
		for(i=0;i<secremain;i++)//复制
		{
			STMFLASH_BUF[i+secoff]=pBuffer[i];
		}
		STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+FLASH_BASE,STMFLASH_BUF,secremain);//写入整个扇区
		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain*8;	//写地址偏移
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/8))secremain=STM_SECTOR_SIZE/8;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}
	};
	HAL_FLASH_Lock();//上锁
}
#endif

/**
  * 函数功能: 从指定地址开始读出指定长度的数据
  * 输入参数: ReadAddr:起始地址
  *           pBuffer:数据指针
  *           NumToRead:半字(16位)数
  * 返 回 值: 无
  * 说    明：无
  */
void STMFLASH_Read ( uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead )
{
	uint16_t i;

	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.
	}
}

/**
  * 函数功能: 向内部flash写入数据测试
  * 输入参数: WriteAddr:起始地址
  *           WriteData:要写入的数据
  * 返 回 值: 无
  * 说    明：无
  */
void Test_Write ( uint32_t WriteAddr, uint64_t WriteData )
{
	STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字
}




/**
 * @brief 读取lash
 *
 * @param Address 要读取的地址
 * @return uint32_t 读取的数据
 */
uint32_t ReadFlash(uint32_t Address) { return *(volatile uint32_t *)Address; }
//
///**
// * @brief 编程Flash（需提前擦除Flash）
// *
// * @param Address 地址
// * @param Data 要写入的数据
// * @return 执行结果，成功返回0
// */
//HAL_StatusTypeDef ProgramFlash(uint32_t Address, uint64_t* data,uint64_t dataLen)
//{
//
//	uint32_t writeAddr = Address,endWriteAddr = (Address+dataLen);
//    HAL_StatusTypeDef _status;
//
//    if(writeAddr<STM32_FLASH_BASE||writeAddr%4) return HAL_ERROR;	//非法地址
//	/* 解锁Flash操作寄存器 */
//	HAL_FLASH_Unlock();
//	/**
//	* FLASH_TYPEPROGRAM_WORD表示以字（32bit）为单位进行写入，除此之外，还有HALFWORLD, DOUBLEWORLD TYPEPROGRAM_WORD FLASH_TYPEPROGRAM_DOUBLEWORD
//	**/
//	while(writeAddr < endWriteAddr) {
//		if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, writeAddr, data)!=HAL_OK) {//写入数据
//			break;
//		}
//		writeAddr += 64;
//		data++;
//	}
//
//	_status = FLASH_WaitForLastOperation(FLASH_WAITETIME);    //等待上次操作完成
//	HAL_FLASH_Lock();
//
//    return _status;
//}
//
/**
 * @brief 擦除Flash
 *
 * @param PageAddress (Page 1-127页)
 * @param NumberPage 要擦除的Page数量每页2k
 * @return 擦除失败的地址，成功返回0xFFFFFFFF
 */
uint32_t EraseFlash(uint32_t pageAddress, uint32_t numberPage)
{
    FLASH_EraseInitTypeDef herase;
    uint32_t _pageError;
    HAL_StatusTypeDef _status;

    if(pageAddress > 127 || pageAddress < 1) return 0xFFFFFFFF;	//非法地址

    herase.TypeErase = FLASH_TYPEERASE_PAGES;	// 按页擦除
    herase.Page = pageAddress;					// 页的起始地址
    herase.NbPages = numberPage;				// 从起始地址开始，要擦除的页数
    herase.Banks = FLASH_BANK_1;
	if (HAL_FLASHEx_Erase(&herase, &_pageError) != HAL_OK){	// PageError为擦除失败时的地址，成功返回0xFFFFFFFF
			return 0;
	}
	_status = FLASH_WaitForLastOperation(FLASH_WAITETIME);    //等待上次操作完成
	if(_status != HAL_OK){
		return _pageError;
	}
    return _pageError;
}
//
////WriteAddr:起始地址(此地址必须为4的倍数!!)
////pBuffer:数据指针
////NumToWrite:字(32位)数(就是要写入的32位数据的个数.)
//void STMFLASH_Write(uint32_t WriteAddr,uint64_t *pBuffer,uint32_t NumToWrite)
//{
//	uint32_t SectorError=0;
//	uint32_t addrx=0;
//	uint32_t erasePage = 0;
//	if(WriteAddr<STM32_FLASH_BASE||WriteAddr%4)return;	//非法地址
//
//	addrx=WriteAddr;				//写入的起始地址
//	erasePage = ((NumToWrite*4 % 2048) != 0)?((NumToWrite*4/2048)+1):(NumToWrite*4/2048);//2k/page
//	if(ReadFlash(addrx)!=0XFFFFFFFF) { //有非0XFFFFFFFF的地方,要擦除这个扇区
//		SectorError = EraseFlash(addrx,erasePage);
//	}
//
//	if(SectorError == 0xFFFFFFFF) {
//		ProgramFlash(addrx,pBuffer,NumToWrite*4);
//	}
//
//}
//
//
//
//
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToRead:字(32位)数
//void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)
//{
//	uint32_t i;
//	for(i=0;i<NumToRead;i++)
//	{
//		pBuffer[i]=ReadFlash(ReadAddr);//读取4个字节.
//		ReadAddr+=4;//偏移4个字节.
//	}
//}
